/*
 * This file is part of the Try! Engine project.
 *
 * Copyright 2010-2011 Emanuele Bertoldi. All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the modified BSD License along with this
 * program. If not, see <http://www.opensource.org/licenses/bsd-license.php>
 */

#include "scenetransformable.h"

using namespace Try;

MessageName SceneTransformable::Msg::sceneNodeChanged = "sceneNodeChanged";
MessageName SceneTransformable::Msg::parentNodeChanged = "parentNodeChanged";
MessageName SceneTransformable::Msg::transformChanged = "transformChanged";

SceneTransformable::~SceneTransformable()
{
    if (m_sceneNode)
        m_sceneNode->removeListener(this);

    delete m_sceneNode;
}

void SceneTransformable::setSceneNode(SceneNode* node)
{
    if (m_sceneNode != node)
    {
        SceneNode* old = m_sceneNode;

        if (m_sceneNode)
            m_sceneNode->removeListener(this);

        m_sceneNode = node;

        if (m_sceneNode)
        {
            m_sceneNode->addListener(this);
            m_sceneNode->resetTransform();
        }

        this->notifyMessage(Message(SceneTransformable::Msg::sceneNodeChanged, old, node));
    }
}

void SceneTransformable::setParentNode(SceneNode* node)
{
    if (m_sceneNode)
        m_sceneNode->setParent(node);
}

void SceneTransformable::resetTransform()
{
    if (m_sceneNode)
        m_sceneNode->resetTransform();
}

void SceneTransformable::setTransform(const Transform& t)
{
    if (m_sceneNode)
        m_sceneNode->setTransform(t);
}

void SceneTransformable::setPosition(const Vector3& pos)
{
    Transform t = this->transform();

    t.position = pos;

    this->setTransform(t);
}

void SceneTransformable::setRotation(const Quaternion& q)
{
    Transform t = this->transform();

    t.rotation = q;

    this->setTransform(t);
}

void SceneTransformable::setRotation(const Vector3& rot)
{
    Transform t = this->transform();

    if (m_sceneNode)
        m_sceneNode->setTransform(t.position, rot, t.scale);
}

void SceneTransformable::setScale(const Vector3& scale)
{
    Transform t = this->transform();

    t.scale = scale;

    this->setTransform(t);
}

void SceneTransformable::translate(const Vector3& dp)
{
    this->setPosition(this->position() + dp);
}

void SceneTransformable::rotate(const Quaternion& dr)
{
    this->setRotation(this->rotation() * dr);
}

void SceneTransformable::rotate(const Vector3& dr)
{
    this->rotate(Quaternion(dr));
}

void SceneTransformable::resize(const Vector3& ds)
{
    this->setScale(this->scale() + ds);
}

void SceneTransformable::lookAt(const Vector3& target)
{
    if (m_sceneNode)
        m_sceneNode->lookAt(target);
}

SceneNode* SceneTransformable::sceneNode() const
{
    return m_sceneNode;
}

SceneNode* SceneTransformable::parentNode() const
{
    if (m_sceneNode)
        return m_sceneNode->parent();

    return 0;
}

Transform SceneTransformable::transform() const
{
    if (m_sceneNode)
        return m_sceneNode->transform();

    return Transform();
}

AxisAlignedBoundingBox SceneTransformable::worldAABB() const
{
    if (m_sceneNode)
        return m_sceneNode->worldAABB();

    return AxisAlignedBoundingBox();
}

Vector3 SceneTransformable::position() const
{
    return this->transform().position;
}

Quaternion SceneTransformable::rotation() const
{
    return this->transform().rotation;
}

Vector3 SceneTransformable::scale() const
{
    return this->transform().scale;
}

void SceneTransformable::onTransformChanged(SceneNode* n, const Transform& t)
{
    if (n == m_sceneNode)
        this->notifyMessage(Message(SceneTransformable::Msg::transformChanged, t));
}

void SceneTransformable::onParentChanged(SceneNode* n, SceneNode* p)
{
    if (n == m_sceneNode)
        this->notifyMessage(Message(SceneTransformable::Msg::parentNodeChanged, p));
}

void SceneTransformable::onDeleting(SceneNode* n)
{
    if (n == m_sceneNode)
        this->setSceneNode(0);
}

std::vector<SceneTransformable*> SceneTransformable::query(SceneNode* node)
{
    std::vector<SceneTransformable*> results;

    if (node)
    {
        std::vector<SceneNodeListener*> listeners = node->listeners();

        for (int i=0; i<node->listenerCount(); i++)
        {
            SceneTransformable* t = dynamic_cast<SceneTransformable*>(listeners.at(i));
            if (t)
                results.push_back(t);
        }
    }

    return results;
}

SceneTransformable::SceneTransformable(const String &name, Object* owner, SceneNode* node)
    : Property(name, owner),
      m_sceneNode(0)
{
    this->setSceneNode(node);
}
